home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Collection of Internet
/
Collection of Internet.iso
/
infosrvr
/
dev
/
scott
/
WWW
/
NextStep
/
Implementation
/
old
/
FileAccess.m
< prev
next >
Wrap
Text File
|
1992-11-25
|
16KB
|
644 lines
// File Access FileAccess.m
// ===========
//
// A HyperAccess object provides access to hyperinformation, using particular
// protocols and data format transformations. This one provides access to
// files in mounted filesystems.
#define takeBackup YES // Take backup when saving existing file Cmd/S
#import "FileAccess.h"
#import <appkit/appkit.h>
#import <stdio.h>
#import <sys/types.h>
#import <sys/stat.h> // For fstat()
#import <libc.h> // For getgroups()
#import "HTUtils.h"
#import "WWW.h" // For WWW_ constants etc
#import "HTParse.h"
#import "HTTCP.h" // TCP utilities, like getting host name
#import "HTFTP.h" // FTP protocol routines
#import "HTFile.h" // File access routines
#define THIS_TEXT (HyperText *)[[[NXApp mainWindow] contentView] docView]
@implementation FileAccess : HyperAccess
#define LENGTH 256
extern char * appDirectory; /* Pointer to directory for application */
// Initialize this class
//
+ initialize
{
return [super initialize];
}
// Overlay method returning the name of the access.
- (const char *)name
{
return "file";
}
//////////////////////////////////////////////////////////////////////////////////////
// S A V I N G
// Save as an HTML file of given name (any format)
// ----------------------------------
//
- save: (HyperText *)HT inFile:(const char *)filename format:(int)format
{
NXStream * s; // The file stream
s = NXOpenMemory(NULL, 0, NX_WRITEONLY);
if (format==WWW_HTML) {
char *saveName = WWW_nameOfFile(filename); // The WWW address
[HT writeSGML:s relativeTo:saveName];
free(saveName);
}
else if (format==WWW_RICHTEXT) [HT writeRichText:s];
else if (format==WWW_PLAINTEXT || format==WWW_SOURCE) [HT writeText:s];
else fprintf(stderr, "HT/File: Unknown format!\n");
if (TRACE) printf("HT file: %li bytes in file `%s' format %i.\n",
NXTell(s), filename, format);
NXFlush(s); /* Try to get over missing end */
NXSaveToFile(s, filename);
NXCloseMemory(s, NX_FREEBUFFER);
return self;
}
// Get Filename near a "hint" node
// -------------------------------
//
// On entry,
// hint is a hypertext whose name is to be used as a hint for
// the user when selecting the name.
// On exit,
// returns 0 if no file selected
// pointer to static string with filename if ok
//
const char * ask_name(HyperText * hint, int format)
{
char * suggestion;
char * slash;
int status;
static SavePanel * save_panel; /* Keep a Save panel alive */
if (!hint) return 0;
if (!save_panel) {
save_panel = [SavePanel new]; // Keep between invocations
}
if (format==WWW_HTML) [save_panel setRequiredFileType: "html"];
else if (format==WWW_RICHTEXT) [save_panel setRequiredFileType: "rtf"];
else if (format==WWW_PLAINTEXT) [save_panel setRequiredFileType: "txt"];
else if (format==WWW_POSTSCRIPT) [save_panel setRequiredFileType: "ps"];
else [save_panel setRequiredFileType: ""];
suggestion = HTLocalName([[hint nodeAnchor]address]);
slash = strrchr(suggestion, '/'); // Point to last slash
if (slash) {
*slash++ = 0; /* Separate directory and filename */
status = [save_panel runModalForDirectory:suggestion file:slash];
} else {
if (TRACE) printf ("No slash in directory!!\n");
status = [save_panel runModalForDirectory:"." file:suggestion];
}
free(suggestion);
if (!status) {
if (TRACE) printf("Save cancelled.\n");
return 0;
}
return [save_panel filename];
}
// Save as a file using save panel (any format)
// --------------------------------
// On entry,
// format Gives the file format to be used (see WWW.h)
// hint is a node to be used for a suggested name.
//
- saveIn:(int)format like:(HyperText *)hint
{
const char * filename; // The name of the file selected
HyperText * HT;
HT = THIS_TEXT;
if (!HT) return HT; // No main window!
filename = ask_name(HT, format);
if (!filename) return nil; // Save clancelled.
return [self save:HT inFile:filename format:format];
}
// Save as an HTML file using save panel
// --------------------
//
- saveAs:sender
{
return [self saveIn:WWW_HTML like:THIS_TEXT];
}
// Save as an RTF file using save panel
// --------------------
//
- saveAsRichText:sender
{
return [self saveIn:WWW_RICHTEXT like:THIS_TEXT];
}
// Save as a plain text file using save panel
// -------------------------
//
- saveAsPlainText:sender
{
return [self saveIn:WWW_PLAINTEXT like:THIS_TEXT];
}
// Save as an HTML file of same name
// ---------------------------------
//
// We don't use a suffix for the backup filename, because some file systems
// (which may be NFS mounted) truncate filenames and can chop the suffix off!
//
- saveNode:(HyperText *) HT
{
char * filename; // The name chosen
id status;
FILE * fp;
const char * addr = [[HT nodeAnchor]address];
filename = HTLocalName(addr);
/* Take a backup before we do anything silly
*/
if (takeBackup) {
char * backup_filename = 0;
char * p = backup_filename;
backup_filename = malloc(strlen(filename)+2);
strcpy(backup_filename, filename);
for(p=backup_filename+strlen(backup_filename);; p--) {// Start at null
if ((*p=='/') || (p<backup_filename)) {
p[1]=','; /* Insert comma after slash */
break;
}
p[1] = p[0]; /* Move up everything to the right of it */
}
if (fp=fopen(filename, "r")) { /* File exists */
fclose(fp);
if (TRACE) printf("File `%s' exists\n", filename);
if (remove(backup_filename)) {
if (TRACE) printf("Backup file `%s' removed\n",
backup_filename);
}
if (rename(filename, backup_filename)) { /* != 0 => Failure */
if (TRACE) printf("Rename `%s' to `%s' FAILED!\n",
filename, backup_filename);
} else { /* Success */
if (TRACE) printf("Renamed `%s' to `%s'\n", filename,
backup_filename);
}
}
free(backup_filename);
} /* if take backup */
status = [self save:HT inFile:filename format:[HT format]];
if (status) [[HT window] setDocEdited: NO];
free(filename);
return status;
}
//////////////////////////////////////////////////////////////////////////////////
//
// O P E N I N G
// LOAD ANCHOR
//
// Returns If successful, the anchor of the node loaded or found.
// If failed, nil.
- loadAnchor: (Anchor *) anAnchor Diagnostic:(int)diagnostic
{
const char * addr = [anAnchor address];
NXStream * s; // The file stream
HyperText * HT; // The new node
char * filename; // The name of the file
char * newname = 0; // The simplified name
int format; // The file format
int file_number = -1; // File number if FTP
// First, we TRY to reduce the name to a unique one.
if (!addr) return nil;
StrAllocCopy(newname, addr);
HTSimplify(newname);
[anAnchor setAddress:newname];
filename = HTLocalName(newname);
addr = [anAnchor address];
if ([anAnchor node]) {
if (TRACE) printf("Anchor %p already has a node\n", anAnchor);
} else {
s = NXMapFile(filename, NX_READONLY); // Map file into memory
if (!s) {
if (TRACE) printf("Could not open file `%s', errno=%i.\n",
filename, errno);
file_number = HTFTP_open_file_read(newname); // Try FTP
if (file_number>=0) {
s = NXOpenFile(file_number, NX_READONLY);
}
}
if (!s) {
if (TRACE) printf("Could not open `%s' with FTP either.\n",
newname);
NXRunAlertPanel(NULL, "Could not open `%s'\n",
NULL,NULL,NULL,
newname /*, strerror(errno) */ );
free(filename);
free(newname);
return nil;
}
free(newname);
// For unrecognised types, open in the Workspace:
format = HTFileFormat(filename);
if (format == WWW_INVALID) {
char command[255]; /* Open in the workspace */
sprintf(command, "open %s", filename);
system(command);
} else { /* Known format */
// Make the new node:
HT = [HyperText newAnchor:anAnchor Server:self];
[HT setupWindow];
[[HT window]setTitle:filename]; // Show something's happening
if (file_number<0)
[HT setEditable:HTEditable(filename)]; // This is editable?
else
[HT setEditable: NO]; // AFTP data.
switch(format) {
case WWW_HTML:
if (diagnostic==2) {
[HT readText:s];
[HT setFormat: WWW_SOURCE];
} else {
[HT readSGML:s diagnostic:diagnostic];
}
break;
case WWW_RICHTEXT:
if (diagnostic > 0) {
[HT readText:s];
[HT setFormat: WWW_SOURCE];
} else {
[HT readRichText:s];
}
break;
case WWW_PLAINTEXT:
[HT readText:s];
break;
}
}
// Clean up now it's on the screen:
if (TRACE) printf("Closing file stream\n");
if (file_number>=0) {
NXClose(s);
HTFTP_close_file(file_number);
} else {
NXCloseMemory(s, NX_FREEBUFFER);
}
} /* If anAnchor not loaded before */
free(filename);
return anAnchor;
}
// Open a file of a given name
// ---------------------------
//
//
// On exit,
// Returns If successful, the anchor of the node loaded or found.
// If failed, nil.
- openFile:(const char *)filename diagnostic:(BOOL)diagnostic
{
Anchor * a;
char * node_address = WWW_nameOfFile(filename);
a = [self loadAnchor:[Anchor newAddress:node_address]
Diagnostic:diagnostic];
free(node_address);
return a;
}
// Ask the User for the name of a file
// -----------------------------------
//
const char * existing_filename()
{
HyperText * HT; // The new node
char * suggestion = 0;
char * slash;
int status;
char * typelist = 0; // Any extension.
static OpenPanel * openPanel=0;
// Get The Filename from the User
if (!openPanel) {
openPanel = [OpenPanel new];
[openPanel allowMultipleFiles:NO];
}
HT = THIS_TEXT; // Hypertext in main window, if selected.
if (HT) {
suggestion = HTLocalName([[HT nodeAnchor]address]);
slash = strrchr(suggestion, '/'); // Point to last slash
if (slash) {
*slash++ = 0; /* Separate directory and filename */
status = [openPanel runModalForDirectory:suggestion
file:""
types:&typelist];
// (was: file:slash but this is silly as that is already open.)
} else {
if (TRACE) printf ("No slash in directory!!\n");
status = [openPanel runModalForDirectory:"."
file:"" types:&typelist];
// (was: file:suggestion but this is silly as above)
}
free(suggestion);
} else {
status = [openPanel runModalForTypes:&typelist];
}
if (!status) {
if (TRACE) printf("Open cancelled\n");
return NULL;
}
return [openPanel filename];
}
// Link to a given file
// --------------------
- linkToFile:sender
{
const char * filename = existing_filename(); // Ask for filename
if (filename) {
char * node_address = WWW_nameOfFile(filename);
Anchor * a = [Anchor newAddress:node_address];
free(node_address);
return [THIS_TEXT linkSelTo:a];
}
return nil;
}
// Open A File using the panel openDiagnostic:
// ---------------------------
//
// On exit,
// Returns If successful, the anchor of the node loaded or found.
// If failed, nil.
- openDiagnostic:(int)diagnostic
{
const char * filename = existing_filename();
if (!filename) {
if (TRACE) printf("Open cancelled\n");
return nil;
}
return [self openFile:filename diagnostic:diagnostic];
}
// Load a personal or system-wide version of a file
// ------------------------------------------------
//
// This accesses the personal copy for the user or, failing that,
// for the system. It is important that this name is fully qualified
// as other names will be generated relative to it.
// We check whether the local version is there before loading it
// in order to prevent error messages being given to the user if
// it is not.
//
// On exit,
// Returns If successful, the anchor of the node loaded or found.
// If failed, nil.
- openMy:(const char *)filename diagnostic:(int)diagnostic
{
Anchor * a;
char name[256];
char template[256];
if (getenv("HOME")) {
int status;
struct stat buf; /* status buffer */
strcpy(name, getenv("HOME"));
strcat(name, "/WWW/");
strcat(name, filename);
status = stat(name, &buf); /* Does file exist? */
if (status>=0) { /* If it does, load it */
if (TRACE) printf("File: stat() returned %d\n", status);
a = [self openFile:name diagnostic:diagnostic];
if (a) {
if ([a node]) [a select];
return a; /* Found local copy */
}
}
}
strcpy(template, appDirectory);
strcat(template, filename);
a = [self openFile:template diagnostic:diagnostic];
if (!a) return nil;
[a setAddress:name]; /* For later save back */
[[a node] setEditable:YES]; /* This is editable data? */
if ([a node]) [a select]; /* Bring to front */
return a;
}
// Go Home
// -------
//
// This accesses the default page of text for the user or, failing that,
// for the system.
//
- goHome:sender
{
return [self openMy:"default.html" diagnostic:0];
}
// Make a new blank node named like the current node
// -------------------------------------------------
//
// On exit,
// Returns If successful, the anchor of the node loaded or found.
// If failed, nil.
- makeNew:sender
{
id status;
HyperText * hint = THIS_TEXT; // The old hypertext
char * node_address; // of the new hypertext
Anchor * a; // The new anchor
const char * filename; // The name of the file selected
HyperText * HT; // The new hypertext
if (!hint) return nil; // No main window!
a = [self openMy:"blank.html" diagnostic:0];
if (!a) return nil; // Couldn't find blank node
[a select];
HT = [a node];
if (!HT) return HT; // No node?!
filename = ask_name(hint, WWW_HTML);
if (!filename) return nil; // Save cancelled.
node_address = WWW_nameOfFile(filename);
[a setAddress:node_address]; // Adopt new address as node name
/* Make a default title for the document from the file name:
**
** On entry,
** node_address must be valid and have a colon in it
** On exit,
** Title is set
** This is destructive of the node_address string.
*/
{
char * pDir = 0; /* Last Directory name */
char * pTitle = strrchr(node_address, '/'); /* File name */
if (pTitle) {
*pTitle++ = 0; /* Chop in two */
pDir = strrchr(node_address, '/');
if (!pDir) pDir = strchr(node_address, ':');
pDir++; /* After delimiter 921122 */
} else {
pTitle = strrchr(node_address, ':')+1;
pDir = "";
}
if (strrchr(pTitle, '.')) *strrchr(pTitle, '.') = 0;// Kill filetype */
if (pDir) {
char title[255];
sprintf(title, "%s -- %s", pTitle, pDir);
[HT setTitle:title]; /* Default title is filename */
} else {
[HT setTitle:pTitle];
}
}
free(node_address);
status = [self save:HT inFile:filename format:WWW_HTML];
if (!status) return nil; // Save failed!
[[a node] setEditable:YES]; // This is editable data.
return a;
}
// Make a new blank node named like the current node and link to it
// ----------------------------------------------------------------
//
- linkToNew:sender
{
HyperText * HT = THIS_TEXT;
Anchor * a;
if (![HT isEditable]) return nil; /* Won't be able to link to it */
a = [self makeNew:sender]; /* Make the new node */
if (!a) return nil;
return [HT linkSelTo:a]; /* Link the selection to it */
}
// Actions:
//
- search:sender
{
return nil;
}
- searchRTF:sender
{
return nil;
}
- searchSGML:sender
{
return nil;
}
// Direct open buttons:
- open:sender
{
return [self openDiagnostic:0];
}
- openRTF:sender
{
return [self openDiagnostic:1];
}
- openSGML:sender
{
return [self openDiagnostic:2];
}
@end